﻿' Copyright © Microsoft Corporation.  All Rights Reserved.
' This code released under the terms of the 
' Microsoft Public License (MS-PL, http://opensource.org/licenses/ms-pl.html.)
Imports Microsoft.VisualBasic
Imports System
Imports System.Collections.Generic
Imports System.IO
Imports System.IO.Packaging
Imports System.Text
Imports System.Reflection
Imports Microsoft.Expression.Encoder
Imports System.Globalization
Imports MakeZip

Namespace ZipPublishExample
	''' <summary>
	''' Class that creates a zip file from a set of files
	''' </summary>
	Friend Class CreateZip
		''' <summary>
		''' Cached publish plug-in object
		''' </summary>
		Private Shared m_plugin As ZipPublishPlugin

		''' <summary>
		''' Create a zip file from a set of files
		''' </summary>
		''' <param name="publish">The publish object which contains the progress and cancel</param>
		''' <param name="files">The list of files to add to the zip</param>
		''' <param name="zipFilename">The filename of the zip file to create</param>
		Friend Shared Sub CreateZipFile(ByVal publish As ZipPublishPlugin, ByVal files() As String, ByVal zipFilename As String)
			m_plugin = publish
			Dim fullFileNames As New List(Of String)()
			Dim escapedFileNames As New List(Of String)()
			CreateFileNameList(files, fullFileNames, escapedFileNames)

			Dim fileCount As Integer = fullFileNames.Count
			Using p As Package = ZipPackage.Open(zipFilename, FileMode.Create)
				For i As Integer = 0 To fullFileNames.Count - 1
					Dim uri As String = escapedFileNames(i)
					Dim part As PackagePart = p.CreatePart(New Uri("/" & uri, UriKind.Relative), "", CompressionOption.Maximum)

					' this is where you'd write your data
					Using outputStream As Stream = part.GetStream(FileMode.OpenOrCreate, FileAccess.Write)
						Using inputStream As FileStream = File.OpenRead(fullFileNames(i))
							EscapeFileNamesAndCopyStream(fullFileNames, escapedFileNames, fullFileNames(i), outputStream)
						End Using
					End Using

					If publish IsNot Nothing AndAlso m_plugin.IsCancelled Then
						Exit For
					End If
				Next i
			End Using

			If m_plugin.IsCancelled Then
				File.Delete(zipFilename)
			End If
		End Sub

		''' <summary>
		''' Copies fullFileName's content into outputStream
		''' </summary>
		''' <param name="fullFileNames">Filename list</param>
		''' <param name="escapedFileNames">List of fixup filenames</param>
		''' <param name="fullFileName">Filename of the data to be copied</param>
		''' <param name="outputStream">Stream to copy the file data into</param>
		Private Shared Sub EscapeFileNamesAndCopyStream(ByVal fullFileNames As List(Of String), ByVal escapedFileNames As List(Of String), ByVal fullFileName As String, ByVal outputStream As Stream)
			Dim extension As String = Path.GetExtension(fullFileName)
			If String.Compare(extension, ".JS", True, CultureInfo.InvariantCulture) = 0 OrElse String.Compare(extension, ".XAML", True, CultureInfo.InvariantCulture) = 0 Then 'ignoreCase - ignoreCase
				ReadFixupTextFile(fullFileNames, escapedFileNames, fullFileName, outputStream)
			Else
				ReadAndWriteFile(fullFileName, outputStream)
			End If
		End Sub

		''' <summary>
		''' Directly copies the file into the outputStream
		''' </summary>
		''' <param name="fullFileName">Filename to copy the data from</param>
		''' <param name="outputStream">Stream to copy the file data into</param>
		Private Shared Sub ReadAndWriteFile(ByVal fullFileName As String, ByVal outputStream As Stream)
			Using inputStream As FileStream = File.OpenRead(fullFileName)
				CopyStream(inputStream, outputStream, 1024 * 200)
			End Using
		End Sub

		''' <summary>
		''' Replaces the filenames usage within "fullFileName"
		''' with their fixup versions in all the script files and copies
		''' the data in the stream
		''' </summary>
		''' <param name="fullFileNames">Filename list</param>
		''' <param name="escapedFileNames">List of fixup filenames</param>
		''' <param name="fullFileName">Filename of the data to be copied</param>
		''' <param name="outputStream">Stream to copy the file data into</param>
		Private Shared Sub ReadFixupTextFile(ByVal fullFileNames As List(Of String), ByVal escapedFileNames As List(Of String), ByVal fullFileName As String, ByVal outputStream As Stream)
			' Check if we need to replace any of the filenames before copying the stream
			Dim fileContents As String = File.ReadAllText(fullFileName)
			For fileIndex As Integer = 0 To fullFileNames.Count - 1
				fileContents = fileContents.Replace(Path.GetFileName(fullFileNames(fileIndex)), escapedFileNames(fileIndex))
			Next fileIndex

			Dim asciiEncoding As New ASCIIEncoding()
			outputStream.Write(asciiEncoding.GetBytes(fileContents), 0, fileContents.Length)
		End Sub

		''' <summary>
		''' Copies the filename list as well as keeping a fixedup version of the filenames
		''' </summary>
		''' <param name="files">Original filename list</param>
		''' <param name="fileNames">Destination filename list</param>
		''' <param name="escapedFileNames">Fixedup filename list</param>
		Private Shared Sub CreateFileNameList(ByVal files() As String, ByVal fileNames As List(Of String), ByVal escapedFileNames As List(Of String))
			Dim fileName As String
			For Each strFile As String In files
				' OPF doesn't like non-legal characters in the filenames
				fileNames.Add(strFile)
				fileName = Path.GetFileName(strFile).Replace(" ", "_")
				fileName = Path.GetFileName(fileName).Replace(";", "_")
				fileName = Path.GetFileName(fileName).Replace("@", "_")
				fileName = Path.GetFileName(fileName).Replace("&", "_")
				fileName = Path.GetFileName(fileName).Replace("=", "_")
				fileName = Path.GetFileName(fileName).Replace("+", "_")
				fileName = Path.GetFileName(fileName).Replace("$", "_")
				escapedFileNames.Add(fileName)
			Next strFile

			Return
		End Sub

		''' <summary>
		''' Copies bufferSize bytes from the input to the output stream
		''' </summary>
		''' <param name="inputStream">Input stream</param>
		''' <param name="outputStream">Output stream</param>
		''' <param name="bufferSize">Number of bytes to copy</param>
		Private Shared Sub CopyStream(ByVal inputStream As Stream, ByVal outputStream As Stream, ByVal bufferSize As Integer)
			Dim buffer(bufferSize - 1) As Byte
			Dim totalWritten As Long = 0
			Dim total As Long = inputStream.Length
			Dim n As Integer
			n = inputStream.Read(buffer, 0, buffer.Length)
			Do While n > 0
				If m_plugin.IsCancelled Then
					Return
				End If

				m_plugin.CallOnProgress("Zipping..", Convert.ToDouble((Convert.ToDecimal(totalWritten) / Convert.ToDecimal(total)) * 100))

				outputStream.Write(buffer, 0, n)
				totalWritten += n
				n = inputStream.Read(buffer, 0, buffer.Length)
			Loop
		End Sub
	End Class
End Namespace
